<?php
/**
 *  给公众号设置模板消息id
 * Author: lyun
 * Date: 2018/5/4
 * Created by Panxsoft.
 */


namespace App\Services\OpenWechat;

use App\Exceptions\ApiErrCode;
use App\Exceptions\ApiException;
use App\Models\OpenWechat\WechatTemplateNotice;
use App\Models\OpenWechat\Authorizer;
use EasyWeChat\OfficialAccount\Application;

class CustomerTemplateNotice
{
    const VALIDATE_INDUSTRY = '消费品';
    const CONSUME_INDUSTRY_ID = 31;

    const IT_FIRST_CLASS = 'IT科技';
    const IT_SECOND_CLASS = '互联网|电子商务';
    const IT_CONSUME_INDUSTRY_ID = 1;

    /**
     * @link https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433747234 全局返回码并没有下方错误码
     * @link https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277 模板消息文档并也有写错误码
     *
     * 都是通过 logger 记录出来的
     */
    const ADD_TPL_ERR_INVALIDATE_TPL_ID = 40037;
    const ADD_TPL_ERR_NUM_LIMIT = 45026;
    const ADD_TPL_ERR_CONFLICT_INDUSTRY = 45027;
    
    
    /**
     * @link https://www.easywechat.com/docs/master/zh-CN/official-account/template_message - easywechat 文档
     * @link https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433751277  - 微信公众号模板消息文档
     *
     * @param Authorizer $authorizer
     * @param string $template_id_short
     *
     * @return string
     *
     * @throws \App\Exceptions\ApiAbstractException
     */
    public static function addAndSaveTemplateId(Authorizer $authorizer, $template_id_short): string
    {
        $app = (new AppFactory())->authorizerApp($authorizer->authorizer_appid);

        $industry = self::getIndustry($app);
            self::validateOrSetNewIndustry($app, $industry);

        $template = $app->template_message->addTemplate($template_id_short);
            self::checkAddTemplateSucceed($template);

        /** 保存template数据到wechat_template_notices表中 */
        $wechat_template_notice = self::assembleTemplateNotice($authorizer, $template, $template_id_short);

        /** @var WechatTemplateNotice $stored */
        $stored = (new WechatTemplateNotice)->create($wechat_template_notice);

        return $stored->template_id;
    }

    /**
     * @param Authorizer $authorizer
     * @param array $template
     * @param $template_id_short
     * @return array
     */
    public static function assembleTemplateNotice(Authorizer $authorizer, array $template, $template_id_short)
    {
        return [
            'authorizer_id'     => $authorizer->id,
            'template_id'       => $template['template_id'],
            'template_id_short' => $template_id_short
        ];
    }

    /**
     * @param array $industry
     *
     * @return bool
     */
    private static function isIndustryValidate(array $industry): bool
    {
        /**
         * 查询行业信息
         *
         * @see Mock::getIndustry()      - 查询行业成功，微信返回数据格式
         * @see Mock::getIndustryError() - 查询行业失败，微信返回数据格式
         */
        if (isset($industry['primary_industry']['first_class'])) {

            // 第一行业或第二行业为IT科技(互联网|电子商务)/消费品
            return ($industry['primary_industry']['first_class'] === self::VALIDATE_INDUSTRY &&
                $industry['primary_industry']['second_class'] === self::VALIDATE_INDUSTRY)
                ||
                ($industry['primary_industry']['first_class'] === self::IT_FIRST_CLASS &&
                    $industry['primary_industry']['second_class'] === self::IT_SECOND_CLASS)
                ||
                ($industry['secondary_industry']['first_class'] === self::VALIDATE_INDUSTRY &&
                    $industry['secondary_industry']['second_class'] === self::VALIDATE_INDUSTRY)
                ||
                ($industry['secondary_industry']['first_class'] === self::IT_FIRST_CLASS &&
                    $industry['secondary_industry']['second_class'] === self::IT_SECOND_CLASS);
        } else {
            return false;
        }
    }

    /**
     * @param array $industry
     *
     * @return bool
     */
    private static function isEmptyIndustry(array $industry): bool
    {
        /**
         * 查询行业信息
         *
         * @see Mock::getIndustry()      - 查询行业成功，微信返回数据格式
         */
        if (isset($industry['primary_industry']['first_class'])) {
            return blank($industry['primary_industry']['first_class']);
        }

        throw new ApiException(ApiErrCode::OPERATE_FAILED, '错误的行业信息');
    }

    /**
     * @param array $industry
     *
     * @return string
     */
    private static function getCurrentIndustryStr(array $industry): string
    {
        /**
         * 查询行业信息
         *
         * @see Mock::getIndustry() - 查询行业成功，微信返回数据格式
         */
        if (isset($industry['primary_industry']['first_class'])) {
            return $industry['primary_industry']['first_class'] . '/' . $industry['primary_industry']['second_class'];
        } else {
            return '';
        }
    }
    
    
    /**
     * @param Application $app
     *
     * @return array|null
     *
     * @throws \App\Exceptions\ApiAbstractException
     */
    private static function getIndustry(Application $app): array
    {
        /**
         * 查询行业信息
         *
         * @see Mock::getIndustry()      - 查询行业成功，微信返回数据格式
         * @see Mock::getIndustryError() - 查询行业失败，微信返回数据格式
         */
        $industry = $app->template_message->getIndustry();

        if (isset($industry['primary_industry']['first_class'])) {
            return $industry;
        } else {
            logger($industry);
            throw new ApiException($industry['errcode'], '无法获取公众号行业信息');
        }
    }

    /**
     * 是否为 IT 行业
     * @param Application $app
     * @return bool
     */
    public static function isIndustryIt(Application $app): bool
    {
        $industry = $app->template_message->getIndustry();

        $prime_first = $industry['primary_industry']['first_class'] ?? null;
        if ($prime_first) {
            $prime_second = $industry['primary_industry']['second_class'] ?? null;
            $sub_first = $industry['secondary_industry']['first_class'] ?? null;
            $sub_second = $industry['secondary_industry']['second_class'] ?? null;

            return ($prime_first === self::IT_FIRST_CLASS && $prime_second === self::IT_SECOND_CLASS)
                || ($sub_first === self::IT_FIRST_CLASS && $sub_second === self::IT_SECOND_CLASS);
        } else {
            return false;
        }
    }
    
    /**
     * @param $industry
     *
     * @throws \App\Exceptions\ApiAbstractException
     */
    private static function checkCanSetNewIndustry(array $industry): void
    {
        if (! self::isEmptyIndustry($industry)) {
            throw new ApiException(
                ApiErrCode::OPERATOR_NOT_ALLOWED,
                '公众号已设置过行业：' . self::getCurrentIndustryStr($industry) . '，请自行修改行业为消费品或IT行业');
        }
    }
    
    /**
     * @param Application $app
     * @param array $industry
     *
     * @throws \App\Exceptions\ApiAbstractException
     */
    private static function validateOrSetNewIndustry(Application $app,
                                                     array $industry): void
    {
        if (self::isIndustryValidate($industry)) {
            return;
        }

        self::checkCanSetNewIndustry($industry);
        /**
         * 设置行业，31：消费品，一月可以改一次
         *
         * @see Mock::setIndustry()      - 设置行业成功，微信返回数据格式
         * @see Mock::setIndustryError() - 设置行业失败，微信返回数据格式
         *
         * TODO 这里可以试下如果公众号只有主行业能不能只修改副行业，需要补全行业代码文档参考链接
         */
        $res_message = $app->template_message
            ->setIndustry(self::CONSUME_INDUSTRY_ID, self::CONSUME_INDUSTRY_ID);

        if ($res_message['errcode']) {
            logger($res_message);
            throw new ApiException($industry['errcode'], '无法设置该公众号行业');
        }
    }
    
    /**
     * @see Mock::addTemplate()      - 添加模板成功，微信返回数据格式
     * @see Mock::addTemplateError() - 添加模板失败，微信返回数据格式
     * @see Mock::addTemplateIndustryError() - 行业冲突
     * @see Mock::addTemplateNumLimit() - 达到上限 26 个不能再申请
     *
     * @param array $template
     * @throws ApiException
     * @throws \App\Exceptions\ApiSilentException
     */
    private static function checkAddTemplateSucceed(array $template): void
    {
        $code = $template['errcode'];
        if (! $code) return;

        logger($template);

        switch ($code) {
            case self::ADD_TPL_ERR_INVALIDATE_TPL_ID  :
                throw new ApiException($code, '错误的模板消息ID');
            case self::ADD_TPL_ERR_NUM_LIMIT  :
                throw new ApiException($code, '申请模板消息已满，请删除再试');
            case self::ADD_TPL_ERR_CONFLICT_INDUSTRY  :
                throw new ApiException($code, '行业冲突，无法设置模板消息');
            default :
                throw new ApiException($code, '申请模板消息失败');
        }
    }
}